package com.leyou.order.service;

import com.leyou.common.auth.entity.UserInfo;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.item.clients.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.entity.Order;
import com.leyou.order.entity.OrderDetail;
import com.leyou.order.entity.OrderLogistics;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.interceptors.UserInfoInterceptor;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.utils.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderService {

    @Autowired
    private UserInfoInterceptor userInfoInterceptor;

    @Autowired
    private UserClient userClient;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private OrderLogisticsMapper orderLogisticsMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private IdWorker idWorker;


    //三张表，订单，订单详情（List<CartDTO> carts，【skuId，num】），物流信息表（addressId）
    //减库存，itemService
    @Transactional
    public Long createOrder(OrderDTO orderDTO){

        //1.先减库存（item）,再下单（order）

        //生成订单编号
        long orderId = idWorker.nextId();
        log.info("【订单编号】生成订单编号：{}",orderId);

        //通过拦截器获取用户信息，获取用户地址
        UserInfo userInfo = userInfoInterceptor.getUserInfo();
        AddressDTO addressDTO = this.userClient.queryAddressById(userInfo.getId(), orderDTO.getAddressId());

        //把用户的地址信息转换为物流信息，暂缺（物流公司、物流单号；可以在后台管理中添加功能实现对这些信息的添加）
        OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO, OrderLogistics.class);

        //需要手动设置订单编号
        orderLogistics.setOrderId(orderId);

        //保存物流信息
        int count = this.orderLogisticsMapper.insertSelective(orderLogistics);
        log.info("【订单系统】保存物流信息：{}",orderId);
        if (1!=count){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        Order orderRecord = new Order();

        List<OrderDetail> orderDetails = new ArrayList<>();

        //根据提交的订单中sku的id集合，查询sku信息

        //cartMap 就是cart集合的map形式，key就是skuID,value就是sku对应的数量
        Map<Long, Integer> cartMap = orderDTO.getCarts().stream()
                .collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));

        //根据sku的id集合查询对应的sku
        List<SkuDTO> skuDTOS = this.itemClient.querySkuIds(new ArrayList<>(cartMap.keySet()));

        //总价
        long total = 0;
        for (SkuDTO skuDTO : skuDTOS) {
            //sku.price * sku.num --- skuPrices
            Integer num = cartMap.get(skuDTO.getId());
            total += skuDTO.getPrice() * num;

            //orderId,skuId,image,id
            OrderDetail orderDetail = BeanHelper.copyProperties(skuDTO, OrderDetail.class);

            orderDetail.setId(null);//在数据库中自增
            orderDetail.setOrderId(orderId);
            orderDetail.setSkuId(skuDTO.getId());
            orderDetail.setImage(StringUtils.substringBefore(skuDTO.getImages(),","));

            orderDetail.setNum(num);//给订单详情中添加商品数量

            //设置创建时间
            orderDetail.setCreateTime(new Date());
            orderDetail.setUpdateTime(orderDetail.getCreateTime());

            orderDetails.add(orderDetail);
        }

        orderRecord.setOrderId(orderId);
        orderRecord.setTotalFee(total);
        //实际支付;总价-优惠+运费
        orderRecord.setActualFee(total);
        //支付类型
        orderRecord.setPaymentType(orderDTO.getPaymentType());
        //邮费0
        orderRecord.setPostFee(0L);
        //userID
        orderRecord.setUserId(userInfo.getId());
        //用户状态
        orderRecord.setStatus(OrderStatusEnum.INIT.value());

        //保存订单状态
        count = this.orderMapper.insertSelective(orderRecord);
        if (1 != count){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //保存订单详情
        count = this.orderDetailMapper.insertList(orderDetails);
        if (orderDetails.size() != count){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }



        //2.数据加工

        //addressId ===>OrderLogistics(订单物流信息)
        //cartDTO===>cartId=====>skuId=====>sku
        //订单中要保存，sku对应的信息，以及，要扣减库存

        //生成order，orderDetail，orderLogistics(物流)

        //System.out.println("orderDTO = " + orderDTO);


        //下单true，减库存false====》rolback
        //下单false，减库存 ===>rolback
        //下单true,减库存true===>commit
        //减库存
        this.itemClient.minusStock(cartMap);
        return orderId;
    }

    /**
     * 通过id 查询订单信息
     * @param orderId
     * @return
     */
    public Order queryOrderById(Long orderId) {
        Order order = this.orderMapper.selectByPrimaryKey(orderId);
        if (null == order){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order;
    }

    private final String KEY_PREFIX = "ly:order:pay:url:";

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private PayHelper payHelper;

    /**
     * 生成支付微信页面
     * @param orderId
     * @return
     */
    public String generatePayUrl(Long orderId) {

        //生成付款链接，每调用一次重新生成一次，但是有效期2小时

        String key = KEY_PREFIX + orderId;

        //如果redis中存在，直接返回
        if (redisTemplate.hasKey(key)){
            return redisTemplate.opsForValue().get(key);
        }

        // 根据订单id，查询订单的详情，目前测试阶段，付款金额写1分，实际情况写actualFee（来自order）
        String payURL = this.payHelper.createOrder(orderId, 1L, "不可描述");

        //保存到redis中，保存2小时
        redisTemplate.opsForValue().set(key,payURL,119, TimeUnit.MINUTES);

        return payURL;
    }


    /**
     * 根据当前订单id生成付款链接
     * @param result
     */
    @Transactional
    public void handleNotify(Map<String, String> result) {
        // 1、签名校验
        try {
            payHelper.isValidSign(result);
        }catch (Exception e){
            log.error("【微信回调】微信签名有误！, result: {}",result, e);
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_SIGN, e);
        }
        // 2、业务校验
        payHelper.checkResultCode(result);

        // 3、校验金额数据
        String totalFeeStr = result.get("total_fee");
        String tradeNo = result.get("out_trade_no");
        if(StringUtils.isEmpty(totalFeeStr) || StringUtils.isEmpty(tradeNo)){
            // 回调参数中必须包含订单编号和订单金额
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        // 3.1 获取结果中的金额
        long totalFee = Long.valueOf(totalFeeStr);
        // 3.2 获取订单
        Long orderId = Long.valueOf(tradeNo);
        Order order = orderMapper.selectByPrimaryKey(orderId);
        // 3.3.判断订单的状态，保证幂等
        if(!order.getStatus().equals(OrderStatusEnum.INIT.value())){
            // 订单已经支付，返回成功
            return;
        }
        // 3.4.判断金额是否一致
        if(totalFee != /*order.getActualPay()*/ 1){
            // 金额不符
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }

        // 4、修改订单状态
        Order orderStatus = new Order();
        orderStatus.setStatus(OrderStatusEnum.PAY_UP.value());
        orderStatus.setOrderId(orderId);
        orderStatus.setPayTime(new Date());
        int count = orderMapper.updateByPrimaryKeySelective(orderStatus);
        if(count != 1){
            log.error("【微信回调】更新订单状态失败，订单id：{}", orderId);
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
        log.info("【微信回调】, 订单支付成功! 订单编号:{}", orderId);
    }

    /**
     * 查询订单状态
     * @param orderId
     * @return
     */
    public Integer queryOrderState(Long orderId) {
        Order order = this.orderMapper.selectByPrimaryKey(orderId);
        if (null == order){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }

    /**
     * 关闭清理订单，并把库存还原
     * 1，查询出所有的未支付的订单
     * 2，筛选已经超过24H未支付的订单
     * 3，关闭此订单，需要查询此订单中占有的商品的库存
     * 4，要统计出，本次订单清理所有的要被添加库存的商品Map<SukId,num>
     * 5，如果某个商品多次从订单中出现则需要统计其数量
     */
    @Transactional
    public void closeOrderOutOfTime(){

        //要加库存商品的集合map，key就是skuId，value就是要加的商品的数量
        Map<Long,Integer> skuMaps = new HashMap<>();

        Order record = new Order();
        //TODO 给status加索引
        record.setStatus(OrderStatusEnum.INIT.value());

        //查询到所有未付款的订单
        List<Order> orders = this.orderMapper.select(record);

        //过期订单筛选
        for (Order order : orders) {
            //如果某个订单的创建时间+24小时比当前时间还早，说明，早就过期了，早就该关闭了
            if (new DateTime(order.getCreateTime()).plusHours(24).isBeforeNow()){
                order.setCloseTime(new Date());
                order.setUpdateTime(new Date());
                order.setStatus(OrderStatusEnum.CLOSED.value());
                //关闭过期订单
                this.orderMapper.updateByPrimaryKeySelective(order);

                //还要把这个订单中所涉及的商品，要进行库存还原
                //根据订单编号，查询订单详情，取出skuId以及对应的num
                OrderDetail orderDetailRecord = new OrderDetail();
                orderDetailRecord.setOrderId(order.getOrderId());

                //查询当前订单所设计的订单详情，
                //TODO 参考商品分组查询时，一次查询所有的组内详情，然后以分组的id，进行分组（JDK，GroupingBy）
                List<OrderDetail> orderDetails = this.orderDetailMapper.select(orderDetailRecord);

                orderDetails.forEach(orderDetail -> {
                    Long skuId = orderDetail.getSkuId();
                    Integer num = orderDetail.getNum();
                    //如果key已经存在，说明加入过
                    if (skuMaps.containsKey(skuId)){
                        //更新数量
                        skuMaps.put(skuId,skuMaps.get(skuId)+num);
                    }else{
                        skuMaps.put(skuId,num);
                    }
                });
            }
        }

        //加库存千万不能重复调用
        this.itemClient.plusStock(skuMaps);
    }
}
